home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume6 / newscnt < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  40.3 KB

  1. Subject:  v06i101:  Count unread news articles (newscnt)
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.UUCP
  4.  
  5. Submitted by: ihnp4!inuxd!gat
  6. Mod.sources: Volume 6, Issue 101
  7. Archive-name: newscnt
  8.  
  9. This program has been used here for several months and seems to be
  10. stable.  I haven't had the opportunity to try it out on other UNIXes.
  11.     Glen A. Taylor
  12.     AT&T (Consumer Products Division)
  13.     Indianapolis, IN  (317) 845-3709
  14.     ihnp4!inuxd!gat
  15.  
  16. [  With Glen's permission, I added the code to use getopt rather than
  17.    scanargs, and the "#ifdef BSD" code; I don't think I broke anything.
  18.    Note that this program uses strtok() -- if it's not in your library,
  19.    the mod.sources archives have two public-domain implementations of
  20.    it and the other string functions.  I haven't tested the program as
  21.    extensively as I might like to, because its utility to me is limited;
  22.    we use notes here.  --r$ ]
  23.  
  24. #!/bin/sh
  25. # This is a shell archive.  Remove anything before this line,
  26. # then unpack it by saving it in a file and typing "sh file".
  27. # Wrapped by rs@mirror.UUCP on Sun Aug  3 19:56:49 EDT 1986
  28. # Contents:  README Makefile newscnt.1 newscnt.c scanargs.c
  29.  
  30. echo x - README
  31. sed 's/^XX//' > "README" <<'@//E*O*F README//'
  32. XXHere is a little utility that I hope others will find as useful as I
  33. XXhave.  Ever since I started to read the net, I have wanted a means to
  34. XXdetermine how much news is waiting for me before I dive into it.  When
  35. XXthe "wn" program came across net.sources a couple months ago, I thought
  36. XXmy problem was solved.  Unfortunately, that program didn't do what I
  37. XXwanted; I am not interested in knowing all of the unread news on the
  38. XXsystem but only the news groups that I read. Consequently I decided to
  39. XXwrite "newscnt."
  40.  
  41. XXNewscnt makes use of your .newsrc file and the system's
  42. XX/usr/lib/news/active file to determine your unread news in much the
  43. XXsame way that readnews or vnews does.  It looks at your "option" line
  44. XXin .newsrc to determine the news groups that interest you and then
  45. XXbuild the structures it needs to count your unread articles.  By
  46. XXdefault, newscnt only returns a total, but with the "-a" option it will
  47. XXgive you a breakdown by newsgroup.  A manual page is included that
  48. XXdescribes all the bells and whistles.
  49.  
  50. XXNewscnt uses the "scanargs" argument parser that was distributed quite
  51. XXsome time ago on this net.  I have included a copy in this distribution
  52. XXfor those who don't have one.
  53.  
  54. XXAny bug reports, suggestions for improvement, other complaints or even
  55. XXpraise should be sent to:
  56. XX    Glen A. Taylor
  57. XX    ihnp4!inuxd!gat
  58. XX    AT&T (Consumer Products Division)
  59. XX    P.O. Box 1008
  60. XX    Indianapolis, IN 46206    (317) 845-3709
  61. @//E*O*F README//
  62. chmod u=rw,g=rw,o=rw README
  63.  
  64. echo x - Makefile
  65. sed 's/^XX//' > "Makefile" <<'@//E*O*F Makefile//'
  66. XX##  If on a BSD system, enable the next line.  Also, make arrangements to
  67. XX##  get strtok(3) pulled in somehow.
  68. XX#BSD    = -DBSD
  69.  
  70. XX##  If you want getopt, enable this line:
  71. XXGETOPT    = -DUSE_GETOPT
  72. XX##  You may have to have something like this line, if getopt isn't in libc.a
  73. XX#GETOBJ    = -lgetopt
  74. XX##  If no getopt, enable this line:
  75. XX#SCAN    = scanargs.o
  76.  
  77. XXCFLAGS=$(BSD) $(GETOPT)
  78.  
  79. XXnewscnt: newscnt.o $(SCAN)
  80. XX    cc -o newscnt newscnt.o $(SCAN) $(GETOBJ)
  81. @//E*O*F Makefile//
  82. chmod u=rw,g=rw,o=rw Makefile
  83.  
  84. echo x - newscnt.1
  85. sed 's/^XX//' > "newscnt.1" <<'@//E*O*F newscnt.1//'
  86. XX.TH NEWSCNT 1 LOCAL
  87. XX.SH NAME
  88. XXnewscnt \- count unread network news based on user's newsgroup
  89. XXchoices
  90. XX.SH SYNOPSIS
  91. XX.B newscnt
  92. XX[ 
  93. XX.BR - { sta }
  94. XX] [
  95. XX.B -x
  96. XX] [
  97. XX.B -f 
  98. XXalt_.newsrc ] [
  99. XX.B -n 
  100. XXnewsgrp ...]
  101. XX.SH DESCRIPTION
  102. XX.I Newscnt
  103. XXprovides a count of unread network news.  By default, the user's .newsrc 
  104. XXfile is examined to determine a list of newsgroups.  These newsgroups
  105. XXare then matched against the list of articles that the user has read
  106. XXand a count of available unread news articles is determined.  The
  107. XXprogram writes to stdout either "No news." if there are no unread news
  108. XXarticles, or "\f2nnn\fP news articles." to indicate the number of
  109. XXunread articles.  Options
  110. XXare provided for altering the format in which the count(s) are
  111. XXreturned and the inputs the program works from.
  112. XX.P
  113. XXThe following options are supported:
  114. XX.TP .5i
  115. XX\f3-\fP{\f3sta\fP}
  116. XXThese three options, only one of which may be specified at a time,
  117. XXaffect the output format of the program.
  118. XX.RS .5i
  119. XX.TP .5i
  120. XX\f3s\fP
  121. XXThe \f2silent\fP option -- nothing is written to stdout.  Only the
  122. XXprogram's normal return codes are provided, e.g. a \f31\fP if there is
  123. XXno unread news, a \f30\fP if there is unread news, or a \f3-1\fP if
  124. XXthere was an error in the processing.
  125. XX.TP .5i
  126. XX\f3t\fP
  127. XXThe \f2terse\fP option -- only the total number of unread news
  128. XXarticles (including zero if there are no unread news articles) is
  129. XXwritten to stdout.  No other text is written.
  130. XX.TP .5i
  131. XX\f3a\fP
  132. XXThe \f2all\fP option -- in addition to a report of the total number of
  133. XXunread news articles, the program writes a line of the form
  134. XX"\f2newsgroup\fP: \f2nnn\fP articles" for each newsgroup having one or
  135. XXmore unread news articles.
  136. XX.RE
  137. XX.TP .5i
  138. XX\f3-x\fP
  139. XXThis flag causes the program to ignore the articles that the user has
  140. XXalready read in each newsgroup and report the total number of articles
  141. XXavailable in the newsgroup.  The effect is similar to the \f3-x\fP
  142. XXflag on most news reading programs.
  143. XX.TP .5i
  144. XX\f3-f \f2alt_.newsrc\fP
  145. XXWhen this option is specified, the file \f2alt_.newsrc\fP is used
  146. XXinstead of the user's \f2$HOME/.newsrc\fP file.
  147. XX.TP .5i
  148. XX\f3-n \f2newsgrps\fP ...
  149. XXWhen this option is specified, the \f2newsgrps\fP specified on the
  150. XXcommand line are reported on.  By default, the program searches
  151. XXthrough the specified or default .newsrc file for a line that begins
  152. XXwith the word \f2options\fP.  This \f2options\fP line is parsed for the
  153. XXlist of \f2newsgrps\fP following a \f3-n\fP argument.  Any
  154. XX\f2newsgrp\fP, either from the command line or from the .newsrc file,
  155. XXprefaced with an exclamation point will be specifically excluded from
  156. XXthe computation.  This is useful for excluding "embedded" newsgroups.
  157. XX(\f3NOTE\fP:  A \f2newsgrp\fP is taken as the "prefix" to a set of
  158. XXnewsgroups and all newsgroups with that prefix are automatically
  159. XXincluded in the count.)
  160. XX.SH FILES
  161. XX/usr/lib/news/active
  162. XX.br
  163. XX$HOME/.newsrc
  164. XX.SH CAVEATS
  165. XXThis program makes use of the /usr/lib/news/active file to determine
  166. XXthe available news items and not the actual news directory's contents.
  167. XXConsequently, its answers may be off slightly if the active file does not
  168. XXaccurately track the available news articles.  If you are told
  169. XXyou have unusually large amounts of unread news, use the -a option to
  170. XXpinpoint the newsgroup(s) with unusual counts
  171. XXand then check to see if there are unexpired
  172. XXarticles causing an unusually large "range" in the active file.
  173. XX.P
  174. XXThe .newsrc format is interpreted according to the author's understanding of 
  175. XXthat file's format.  Variations in how this file is interpreted may exist.
  176. XX.SH AUTHOR
  177. XX.nf
  178. XXGlen A. Taylor
  179. XXAT&T (Consumer Products Divsion)
  180. XXMay 1986
  181. XX.fi
  182. @//E*O*F newscnt.1//
  183. chmod u=rw,g=rw,o=rw newscnt.1
  184.  
  185. echo x - newscnt.c
  186. sed 's/^XX//' > "newscnt.c" <<'@//E*O*F newscnt.c//'
  187. XX/* newscnt -- a program to enumerate unread net.news articles     *
  188. XX *                                                                *
  189. XX *   Glen A. Taylor (AT&T-IS)         May 1986                    *
  190. XX *                                                                *
  191. XX * Added the code inside the BSD and USE_GETOPT #ifdef's      *
  192. XX *   Rich $alz (Mirror Systems)       June 1986                   *
  193. XX *                                                                */
  194.  
  195.  
  196. XXstatic char sccsid[] = "@(#)newscnt.c    1.2";
  197.  
  198. XX#define BSD
  199. XX#define DEBUG
  200.  
  201. XX#include <stdio.h>
  202. XX#ifdef BSD
  203. XX#include <strings.h>
  204. XXchar *strtok();
  205. XX#else
  206. XX#include <string.h>
  207. XX#endif
  208. XX#include <pwd.h>
  209. XX#include <ctype.h>
  210.  
  211. XX#define FALSE    0
  212. XX#define TRUE    1
  213. XX#define SILENT  4
  214. XX#define TERSE   2
  215. XX#define ALL     1
  216. XX#define ERROR   -1
  217. XX#define NEWS    0
  218. XX#define NONEWS  1
  219. XX#define MAXLENG 512
  220. XX#define MAXGRPS 128
  221. XX#define ACTIVE  "/usr/lib/news/active"
  222. XX#define NEWSRC  ".newsrc"
  223. XX#ifdef BSD
  224. XX#define USER_NAME "USER"
  225. XX#else
  226. XX#define USER_NAME    "LOGNAME"
  227. XX#endif
  228.  
  229. XXstruct grp {
  230. XX    char *name;   /* news group name */
  231. XX    long  lowest; /* lowest article number */
  232. XX    long  highest; /* highest article number */
  233. XX    int   done; /* to indicate this element has been processed */
  234. XX    struct grp *ptr; /* ptr. to next element */
  235. XX};
  236.  
  237. XXtypedef struct grp *GRPTR;
  238.  
  239. XXchar *namevec[MAXGRPS]; /* array of newsgroup name strings */
  240.  
  241. XXmain(argc,argv)
  242. XXint argc;
  243. XXchar **argv;
  244. XX{
  245.  
  246. XX    char *alt_newsrc,  **newsgrps, *malloc();
  247. XX    int silent, terse, all, flags, fflag, nflag, xflag, nr_newsgrps;
  248. XX    FILE *fpnewsrc, *fpactive;
  249. XX    int count = 0;
  250. XX    GRPTR listhead = NULL;
  251.  
  252. XX    /* Parse arguments and establish working parameters */
  253. XX#ifdef    USE_GETOPT
  254. XX    {
  255. XX    char c;
  256. XX    extern char *optarg;
  257. XX    extern int optind;
  258.  
  259. XX    nr_newsgrps = silent = terse = all = xflag = fflag = nflag = flags = 0;
  260. XX    while ((c = getopt(argc, argv, "staxf:n:")) != EOF)
  261. XX        switch (c) {
  262. XX        default:
  263. XXUsage:
  264. XX            fprintf(stderr,
  265. XX               "usage: %s [-{sta}] [-x] [-f alt_.newsrc] -n newsgrp...",
  266. XX               argv[0]);
  267. XX            exit(1);
  268. XX        case 's': silent = 1; break;
  269. XX        case 't': terse = 1; break;
  270. XX        case 'a': all = 1; break;
  271. XX        case 'x': xflag = 1; break;
  272. XX        case 'f': fflag = 1; alt_newsrc = optarg; break;
  273. XX        case 'n': nflag = 1; nr_newsgrps += addgrp(optarg); break;
  274. XX        }
  275. XX    if (silent + terse + all > 1)
  276. XX        goto Usage;
  277. XX    for (argv += optind; *argv; argv++)
  278. XX        nr_newsgrps += addgrp(argv);
  279. XX    }
  280. XX#else    /* !USE_GETOPT */
  281. XX    if (!scanargs(argc,argv, "% sta%- x%- f%-alt_.newsrc!s n%-newsgrp!*s",
  282. XX     &flags, &xflag, &fflag, &alt_newsrc, &nflag, &nr_newsgrps, &newsgrps))
  283. XX     exit(1);
  284. XX    silent = flags & SILENT;
  285. XX    terse  = flags & TERSE;
  286. XX    all    = flags & ALL;
  287. XX#endif    /* USE_GETOPT */
  288.  
  289. XX    /* Open the .newsrc file */
  290. XX    if (fflag) { /* use the file name the user supplied */
  291. XX    fpnewsrc = fopen(alt_newsrc,"r");
  292. XX    }
  293. XX    else { /* determine the user's .newsrc file and open */
  294. XX    {
  295. XX        char *fname[MAXLENG];
  296. XX        char *uname, *getenv();
  297. XX        struct passwd *passptr, *getpwnam();
  298.  
  299. XX        uname = getenv(USER_NAME); /* get user's login name */
  300. XX        passptr = getpwnam(uname); /* get user's home dir */
  301. XX        /* construct .newsrc path */
  302. XX        strcpy(fname, passptr -> pw_dir);
  303. XX        strcat(fname, "/");
  304. XX        strcat(fname, NEWSRC);
  305.  
  306. XX        fpnewsrc = fopen(fname,"r");
  307. XX    }
  308. XX    }
  309.  
  310. XX    /* Derive array of newsgroup names to search for */
  311. XX    if (!nflag) getgrps(fpnewsrc, &nr_newsgrps, &newsgrps);
  312.  
  313. XX#ifdef DEBUG
  314. XX    { int i;
  315. XX      printf("Number of newsgroups = %d\n", nr_newsgrps);
  316. XX      for (i = 0; i < nr_newsgrps; i++) printf("%s\n", newsgrps[i]);
  317. XX    }
  318. XX#endif
  319.  
  320. XX    if (nr_newsgrps == 0) exit (ERROR); /* can't do anything! */
  321.  
  322. XX    /* Open the ACTIVE newsgroup file */
  323. XX    if ((fpactive = fopen(ACTIVE, "r"))==NULL)
  324. XX    exit(ERROR); /* cannot proceed w/o ACTIVE file */
  325.  
  326. XX    /* Go through the ACTIVE file and make linked list of all 
  327. XX       newsgroups matching elements of the "newsgrps" array   */
  328. XX    {
  329. XX    char gname[MAXLENG];   /* tmp to hold active group name */
  330. XX    long low, high;  /* tmps to hold active group low & high numbers */
  331. XX    int i, j;
  332. XX    char *sp, junk[10];
  333. XX    GRPTR current, next;
  334.  
  335. XX    while (fscanf(fpactive,"%s %ld %ld %s\n", gname, &high, &low, junk)
  336. XX        != EOF) { /* read the entire ACTIVE file */
  337.  
  338. XX        /* Compare gname to every name in newsgrps */
  339. XX        for (i=0; i < nr_newsgrps; i++) {
  340. XX        sp = newsgrps[i];
  341. XX        j = strlen(sp);
  342. XX        /* check for a group to be explicitly skipped */
  343. XX        if (*sp == '!') { 
  344. XX            sp++;
  345. XX            if (strncmp(gname,sp,j-1) == 0) break;
  346. XX        }
  347. XX        /* otherwise check for a positive match */
  348. XX        else if (strncmp(gname,sp,j) == 0) {
  349. XX        /* got a matching group so allocate new list element */
  350. XX            next = (struct grp *) malloc(sizeof (struct grp));
  351.  
  352. XX            if (!listhead) {/* first element */
  353. XX            listhead = next;
  354. XX            current = next;
  355. XX            }
  356. XX            else {
  357. XX            current -> ptr = next; /* link on this element */
  358. XX            current = next;
  359. XX            }
  360. XX            /* fill in the list element structure */
  361. XX            j = strlen(gname) + 1; /* get length of group name */
  362. XX            current -> name = strcpy(malloc(j), gname);
  363. XX            current -> lowest = low;
  364. XX            current -> highest = high;
  365. XX            current -> done = FALSE;
  366. XX            current -> ptr = NULL;
  367.  
  368. XX            /* terminate inner loop -- proceed to next group */
  369. XX            break;
  370. XX        }
  371. XX        }
  372. XX    }
  373. XX    }
  374.  
  375. XX#ifdef DEBUG
  376. XX    {
  377. XX    GRPTR lptr;
  378. XX    printf("\nList of relevant active newsgroups:\n");
  379. XX    lptr = listhead;
  380. XX    while (lptr != NULL) {
  381. XX        printf("%s %d %d\n",lptr->name,lptr->highest,lptr->lowest);
  382. XX        lptr = lptr->ptr;
  383. XX    }
  384. XX    }
  385. XX#endif
  386.  
  387. XX    /* Work down the linked list and compare to .newsrc entries */
  388. XX    rewind(fpnewsrc);
  389. XX    {
  390. XX    GRPTR next;
  391. XX    char line[MAXLENG], *gname, *range;
  392. XX    int ignore, cnt;
  393.  
  394. XX    while(fgets(line, MAXLENG, fpnewsrc) != NULL){
  395. XX        gname = line; /* name starts in first char position */
  396. XX        range = line; /* find the "range" list */
  397. XX        while (*range != ':' && *range != '!' && *range != '\0')
  398. XX        range++; /* : and ! are the two legal name delimiters */
  399. XX        ignore = *range == '!' ? TRUE : FALSE; /* do we ignore? */
  400. XX        *range++ = '\0'; /* delimit gname from range */
  401.  
  402. XX        /* Search the list for this newsgroup */
  403. XX        next = listhead;
  404. XX        while (next != NULL) {
  405. XX        if (strcmp(gname,next -> name) == 0) {/* Found it */
  406. XX            if (!ignore) {
  407. XX                cnt = ckrange(next, range, xflag);
  408. XX            count = count + cnt;
  409. XX            if (all && cnt) printf("%s: %d articles\n",gname,cnt);
  410. XX            }
  411. XX            next -> done = TRUE;
  412. XX            break;
  413. XX        }
  414. XX        next = next -> ptr;
  415. XX        }
  416. XX    }
  417. XX    /* go through list one more time and get those that aren't "done" */
  418. XX    next = listhead;
  419. XX    while (next != NULL) {
  420. XX        if (! next -> done) {
  421. XX        cnt = next -> highest - next -> lowest;
  422. XX        if (cnt < 0) cnt = 0;
  423. XX        count = count + cnt;
  424. XX        /* print the nonzero entries if "all" were requested */
  425. XX        if (all && cnt) printf("%s: %d articles\n",next->name,cnt);
  426. XX        }
  427. XX        next = next -> ptr;
  428. XX    }
  429. XX    }
  430.  
  431. XX    /* Print final total */
  432. XX    if (silent) exit( count? NEWS : NONEWS);
  433. XX    if (terse) printf("%d\n", count);
  434. XX    else if (count) printf("%d news articles.\n",count);
  435. XX    else printf("No news.\n");
  436. XX    exit(count? NEWS : NONEWS);
  437. XX}
  438.  
  439. XX#ifdef    USE_GETOPT
  440. XX/* addgrp -- add a group list to the namevec global array.
  441. XX         returns count of groups added.            */
  442. XXaddgrp(p)
  443. XXchar *p;
  444. XX{
  445. XX    static char SEPARATORS[] = " \t\n,";
  446. XX    static int cnt;
  447. XX    char *ptr;
  448. XX    int i;
  449.  
  450. XX    ptr = strtok(p, SEPARATORS);
  451. XX    for (i = 0; ptr = strtok(NULL, SEPARATORS); i++)
  452. XX    namevec[cnt++] = strcpy(malloc(strlen(ptr) + 1), ptr);
  453. XX    return(i);
  454. XX}
  455. XX#endif    /* USE_GETOPT */
  456.  
  457.  
  458. XX/* getgrps -- reads the .newsrc file, finds the "option -n ... " news
  459. XX          group list, and copies news groups into an array of
  460. XX          strings.                                                */
  461. XXgetgrps(fp, num, names)
  462. XXFILE *fp;
  463. XXint *num;
  464. XXchar ***names;
  465. XX{
  466. XX    char line[MAXLENG], *ptr;
  467. XX    int optflag = FALSE, nflag = FALSE;
  468.  
  469. XX    *names = namevec;
  470. XX    *num = 0;
  471. XX    while (fgets(line,MAXLENG,fp) != NULL ) {
  472. XX    if (!optflag && strncmp(line,"option",6) == 0) { 
  473. XX        /* found an option line */
  474. XX        optflag = TRUE;
  475. XX        nflag = FALSE;
  476. XX        ptr = strtok(line," \t\n");
  477. XX        ptr = strtok(NULL," \t\n"); /* skip "option" */
  478. XX    }
  479. XX    else if (optflag && isspace(line[0])) {
  480. XX        /* found a continuation line */
  481. XX        ptr = strtok(line," \t\n");
  482. XX    }
  483. XX    else {
  484. XX        optflag = FALSE;
  485. XX        nflag = FALSE;
  486. XX    }
  487.  
  488. XX    /* Parse the options line */
  489. XX    if (optflag) {
  490. XX        while (!nflag && ptr != NULL) {
  491. XX        if (strcmp(ptr,"-n") == 0) {
  492. XX            nflag = TRUE;
  493. XX            ptr = strtok(NULL, " \t\n"); /* consume "-n" */
  494. XX            break;
  495. XX        }
  496. XX        ptr = strtok(NULL, " \t\n"); /* get first news group */
  497. XX        }
  498. XX        while (nflag && ptr != NULL) {
  499. XX        /* copy this news group into "newsgrps" */
  500. XX        namevec[*num] = strcpy(malloc(strlen(ptr)+1), ptr);
  501. XX        *num += 1;
  502. XX        ptr = strtok(NULL, " \t\n");
  503. XX        }
  504. XX    }
  505. XX    }
  506. XX}
  507.  
  508. XX/* ckrange -- compare a range specification against a high count / low
  509. XX          count and return the number of unread articles          */
  510. XXint
  511. XXckrange(ptr, range, flag)
  512. XXGRPTR ptr;
  513. XXchar *range;
  514. XXint flag;
  515. XX{
  516. XX    long low, high, nxthi, nxtlo;
  517. XX    int i, extent, count;
  518. XX    char *bitmap, *nextseg(), *rp;
  519.  
  520. XX#ifdef DEBUG
  521. XX    printf("<*> ckrange: \n\tname -- %s\n\thigh = %d\n\tlow = %d\n",
  522. XX    ptr->name, ptr->highest, ptr->lowest);
  523. XX    printf("\trange -- %s\n", range);
  524. XX#endif
  525.  
  526. XX    /* get low and high values for the newsgroup */
  527. XX    low = ptr -> lowest;
  528. XX    high = ptr -> highest;
  529. XX    if (low > high) return (0); /* this one is screwy! */
  530.  
  531.  
  532. XX    if (flag) { /* ignore range info */
  533. XX    count = high - low;
  534. XX    if (count < 0 ) count = 0;
  535. XX    return (count);
  536. XX    }
  537.  
  538. XX    /* set up a bitmap to do the computation */
  539. XX    extent = high - low + 1;
  540. XX    if (extent <= 0) return (0);
  541. XX    bitmap = malloc(extent);
  542. XX    if (bitmap == NULL) exit(ERROR);
  543. XX    for (i=0; i < extent; i++) bitmap[i] = '*'; /* init. bitmap */
  544.  
  545. XX    /* set bits (bytes, actually) for each read article */
  546. XX    rp = range;
  547. XX    while ((rp = nextseg(&nxthi,&nxtlo,rp)) != NULL) {
  548. XX#ifdef DEBUG
  549. XX    printf("<*> nexthi = %d, nextlo = %d\nnext seg = `%s'\n",
  550. XX    nxthi, nxtlo, rp);
  551. XX#endif
  552.  
  553. XX    /* check that the ones read intersect the ones avail */
  554. XX    if (nxthi < low || nxtlo > high) continue;
  555.  
  556. XX    /* adjust the range markers to the intersection */
  557. XX    nxthi = (high > nxthi) ? nxthi : high;
  558. XX    nxtlo = (low < nxtlo) ? nxtlo : low;
  559.  
  560. XX    /* mark the articles that were read */
  561. XX    if (nxtlo == nxthi) bitmap[nxtlo-low] = ' ';
  562. XX    else for (i=nxtlo-low; i <= nxthi-low; i++) bitmap[i] = ' ';
  563. XX    }
  564.  
  565. XX    /* count the set bits & go home */
  566. XX    count = 0;
  567. XX    for (i=0; i < extent; i++) if (bitmap[i] == '*') count++;
  568. XX    free(bitmap);
  569. XX    return (count);
  570. XX}
  571.  
  572. XXchar *
  573. XXnextseg(high, low, str)
  574. XXlong *high, *low;
  575. XXchar *str;
  576. XX{
  577. XX    char *p1, *retval;
  578. XX    long val = 0L;
  579. XX    int hyphen = FALSE, more = TRUE;
  580.  
  581. XX    if (*str == '\0') return(NULL);
  582. XX    p1 = str;
  583. XX    while (more) {
  584. XX    switch (*p1) {
  585. XX    case '0':
  586. XX    case '1':
  587. XX    case '2':
  588. XX    case '3':
  589. XX    case '4':
  590. XX    case '5':
  591. XX    case '6':
  592. XX    case '7':
  593. XX    case '8':
  594. XX    case '9':
  595. XX            val = val * 10 + (*p1 - '0');
  596. XX            break;
  597.  
  598. XX    case '-':
  599. XX            *low = val;
  600. XX            val = 0L;
  601. XX            hyphen = TRUE;
  602. XX            break;
  603.  
  604. XX    case ' ':
  605. XX    case ',':
  606. XX    case '\n':
  607. XX            more = FALSE;
  608. XX            retval = ++p1;
  609. XX            *high = val;
  610. XX            if (!hyphen) *low = val;
  611. XX            break;
  612.  
  613. XX    case '\0':
  614. XX            more = FALSE;
  615. XX            retval = NULL;
  616. XX            *high = val;
  617. XX            if (!hyphen) *low = val;
  618. XX            return (retval);
  619. XX    }
  620. XX    p1++;
  621. XX    }
  622. XX    return (retval);
  623. XX}
  624. @//E*O*F newscnt.c//
  625. chmod u=rw,g=rw,o=rw newscnt.c
  626.  
  627. echo x - scanargs.c
  628. sed 's/^XX//' > "scanargs.c" <<'@//E*O*F scanargs.c//'
  629. XX/* 
  630. XX * $Header: scanargs.c,v 1.3 83/05/22 02:21:32 thomas Exp $
  631. XX *         Version 7 compatible
  632. XX *     Argument scanner, scans argv style argument list.
  633. XX * 
  634. XX *     Some stuff is a kludge because sscanf screws up
  635. XX * 
  636. XX *     Gary Newman - 10/4/1979 - Ampex Corp. 
  637. XX * 
  638. XX *     Modified by Spencer W. Thomas, Univ. of Utah, 5/81 to
  639. XX *     add args introduced by     a flag, add qscanargs call,
  640. XX *     allow empty flags.
  641. XX * 
  642. XX *     Compiling with QUICK defined generates 'qscanargs' ==
  643. XX *     scanargs w/o floating point support; avoids huge size
  644. XX *     of scanf.
  645. XX * 
  646. XX *     If you make improvements we'd like to get them too.
  647. XX *     Jay Lepreau    lepreau@utah-20, decvax!harpo!utah-cs!lepreau
  648. XX *     Spencer Thomas    thomas@utah-20, decvax!harpo!utah-cs!thomas 
  649. XX * 
  650. XX *    (I know the code is ugly, but it just grew, you see ...)
  651. XX * 
  652. XX * Modified by:    Spencer W. Thomas
  653. XX *     Date:    Feb 25 1983
  654. XX * 1. Fixed scanning of optional args.  Now args introduced by a flag
  655. XX *    must follow the flag which introduces them and precede any other
  656. XX *    flag argument.  It is still possible for a flag introduced
  657. XX *    argument to be mistaken for a "bare" argument which occurs
  658. XX *    earlier in the format string.  This implies that flags may not
  659. XX *    be conditional upon other flags, and a message will be generated
  660. XX *    if this is attempted.
  661. XX * 
  662. XX * 2. Usage message can be formatted by inserting newlines, tabs and
  663. XX *    spaces into the format string.  This is especially useful for
  664. XX *    long argument lists.
  665. XX * 
  666. XX * 3. Added n/N types for "numeric" args.  These args are scanned
  667. XX *    using the C language conventions - a number starting 0x is
  668. XX *    hexadecimal, a number starting with 0 is octal, otherwise it is
  669. XX *    decimal.
  670. XX */
  671.  
  672. XX#include <stdio.h>
  673. XX#include <ctype.h>
  674. XX#include <varargs.h>
  675.  
  676. XXtypedef char bool;
  677. XX/* 
  678. XX * An explicit assumption is made in this code that all pointers look
  679. XX * alike, except possible char * pointers.
  680. XX */
  681. XXtypedef int *ptr;
  682.  
  683. XX#define YES 1
  684. XX#define NO 0
  685. XX#define ERROR(msg)  {fprintf(stderr, "msg\n"); goto error; }
  686.  
  687. XX/* 
  688. XX * Storage allocation macros
  689. XX */
  690. XX#define NEW( type, cnt )    (type *) malloc( (cnt) * sizeof( type ) )
  691. XX#define RENEW( type, ptr, cnt )    (type *) realloc( ptr, (cnt) * sizeof( type ) )
  692.  
  693. XXstatic char * prformat();
  694. XXstatic bool isnum();
  695.  
  696. XX/* 
  697. XX * Argument list is (argc, argv, format, ... )
  698. XX */
  699. XX#ifndef    QUICK
  700. XXscanargs ( va_alist )
  701. XX#else
  702. XXqscanargs ( va_alist )
  703. XX#endif
  704. XXva_dcl
  705. XX{
  706. XX    int     argc;            /* Actual arguments */
  707. XX    char  **argv;
  708. XX    char   *format;
  709. XX    va_list argl;
  710.  
  711. XX    register    check;            /* check counter to be sure all argvs
  712. XX                       are processed */
  713. XX    register char  *cp;
  714. XX    register    cnt;
  715. XX    int        optarg = 0;            /* where optional args start */
  716. XX    int        nopt;
  717. XX    char    tmpflg,            /* temp flag */
  718. XX        typchr;            /* type char from format string */
  719. XX    char    c;
  720. XX    bool  * arg_used;            /* array of flags */
  721. XX    char  * malloc();
  722. XX    ptr        aptr;            /* pointer to return loc */
  723.  
  724. XX    bool    required;
  725. XX    int        excnt;            /* which flag is set */
  726. XX    bool    exflag;            /* when set, one of a set of exclusive
  727. XX                       flags is set */
  728.  
  729. XX    bool    list_of;            /* set if parsing off a list of args */
  730. XX    bool    comma_list;            /* set if AT&T style multiple args */
  731. XX    int      * cnt_arg;            /* where to stuff list count */
  732. XX    int        list_cnt;            /* how many in list */
  733. XX    /* These are used to build return lists */
  734. XX    char ** strlist;
  735. XX    int   * intlist;
  736. XX    long  * longlist;
  737. XX    float * fltlist;
  738. XX    double *dbllist;
  739.  
  740. XX    char   *ncp;            /* remember cp during flag scanning */
  741. XX#ifndef    QUICK
  742. XX    char   *cntrl;            /* control string for scanf's */
  743. XX    char    junk[2];            /* junk buffer for scanf's */
  744.  
  745. XX    cntrl = "% %1s";            /* control string initialization for
  746. XX                       scanf's */
  747. XX#endif
  748.  
  749. XX    va_start( argl );
  750. XX    argc = va_arg( argl, int );
  751. XX    argv = va_arg( argl, char ** );
  752. XX    format = va_arg( argl, char * );
  753.  
  754. XX    arg_used = NEW( bool, argc );
  755. XX    if (arg_used == NULL)
  756. XX    {
  757. XX    fprintf(stderr, "malloc failed in scanargs, exiting\n");
  758. XX    exit(-1);
  759. XX    }
  760. XX    else
  761. XX    {
  762. XX    for (cnt=0; cnt<argc; cnt++)
  763. XX        arg_used[cnt] = NO;
  764. XX    }
  765.  
  766. XX    check = 0;
  767. XX    cp = format;
  768. XX    /* 
  769. XX     * Skip program name
  770. XX     */
  771. XX    while ( *cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0' )
  772. XX    cp++;
  773.  
  774. XX    while (*cp)
  775. XX    {
  776. XX    required = NO;            /* reset per-arg flags */
  777. XX    list_of = NO;
  778. XX    comma_list = NO;
  779. XX    list_cnt = 0;
  780. XX    switch (*(cp++))
  781. XX    {
  782. XX        default:             /* all other chars */
  783. XX        break;
  784. XX        case ' ':            /* separators */
  785. XX        case '\t':
  786. XX        case '\n':
  787. XX        optarg = 0;        /* end of optional arg string */
  788. XX        break;
  789.  
  790. XX        case '!':             /* required argument */
  791. XX        required = YES;
  792. XX        case '%':             /* not required argument */
  793. XXreswitch:                /* after finding '*' or ',' */
  794. XX        switch (typchr = *(cp++))
  795. XX        {
  796. XX            case ',':        /* argument is AT&T list of things */
  797. XX            comma_list = YES;
  798. XX            case '*':        /* argument is list of things */
  799. XX            list_of = YES;
  800. XX            list_cnt = 0;    /* none yet */
  801. XX            cnt_arg = va_arg( argl, int *);    /* item count * here */
  802. XX            goto reswitch;    /* try again */
  803.  
  804. XX            case '$':        /* "rest" of argument list */
  805. XX            while ( argc > 1 && !arg_used[argc-1] )
  806. XX                argc--;    /* find last used argument */
  807. XX            *va_arg( argl, int * ) = argc;
  808. XX            break;
  809.  
  810. XX            case '-':         /* argument is flag */
  811. XX            if (optarg > 0)
  812. XX                ERROR(Format error: flag conditional on flag not allowed);
  813.  
  814. XX            /* go back to label */
  815. XX            ncp = cp-1;    /* remember */
  816. XX            cp -= 3;
  817. XX            for (excnt = exflag = 0
  818. XX                ; *cp != ' ' && !(*cp=='-' &&(cp[-1]=='!'||cp[-1]=='%'));
  819. XX                (--cp, excnt++))
  820. XX            {
  821. XX                for (cnt = optarg+1; cnt < argc; cnt++)
  822. XX                {
  823. XX                /* flags all start with - */
  824. XX                if (*argv[cnt] == '-' && !arg_used[cnt] &&
  825. XX                    !isdigit(argv[cnt][1]))
  826. XX                    if (*(argv[cnt] + 1) == *cp)
  827. XX                    {
  828. XX                    if (*(argv[cnt] + 2) != 0)
  829. XX                        ERROR (extra flags ignored);
  830. XX                    if (exflag)
  831. XX                        ERROR (more than one exclusive flag chosen);
  832. XX                    exflag++;
  833. XX                    required = NO;
  834. XX                    check += cnt;
  835. XX                    arg_used[cnt] = 1;
  836. XX                    nopt = cnt;
  837. XX                    *va_arg( argl, int *) |= (1 << excnt);
  838. XX                    break;
  839. XX                    }
  840. XX                }
  841. XX            }
  842. XX            if (required)
  843. XX                ERROR (flag argument missing);
  844. XX            cp = ncp;
  845. XX            /* 
  846. XX             * If none of these flags were found, skip any
  847. XX             * optional arguments (in the varargs list, too).
  848. XX             */
  849. XX            if (!exflag)
  850. XX            {
  851. XX                va_arg( argl, int * );    /* skip the arg, too */
  852. XX                while (*++cp && ! isspace(*cp))
  853. XX                if (*cp == '!' || *cp == '%')
  854. XX                {
  855. XX                    if ( *++cp == '*' || *cp == ',' )
  856. XX                    {
  857. XX                    cp++;
  858. XX                    va_arg( argl, int * );
  859. XX                    }
  860. XX                    /* 
  861. XX                     * Assume that char * might be a
  862. XX                     * different size, but that all
  863. XX                     * other pointers are same size.
  864. XX                     */
  865. XX                    if ( *cp == 's' )
  866. XX                    va_arg( argl, char * );
  867. XX                    else
  868. XX                    va_arg( argl, ptr );
  869. XX                }
  870. XX            }
  871. XX            else
  872. XX            {
  873. XX                optarg = nopt;
  874. XX                cp++;    /* skip over - */
  875. XX            }
  876.  
  877. XX            break;
  878.  
  879. XX            case 's':         /* char string */
  880. XX            case 'd':         /* decimal # */
  881. XX            case 'o':         /* octal # */
  882. XX            case 'x':         /* hexadecimal # */
  883. XX            case 'n':        /* "number" in C syntax */
  884. XX#ifndef    QUICK
  885. XX            case 'f':         /* floating # */
  886. XX#endif
  887. XX            case 'D':         /* long decimal # */
  888. XX            case 'O':         /* long octal # */
  889. XX            case 'X':         /* long hexadecimal # */
  890. XX            case 'N':        /* long number in C syntax */
  891. XX#ifndef    QUICK
  892. XX            case 'F':         /* double precision floating # */
  893. XX#endif
  894. XX            for (cnt = optarg+1; cnt < argc; cnt++)
  895. XX            {
  896. XX                ncp = argv[cnt];
  897.  
  898. XX                if ( isnum( ncp, typchr, comma_list ) )
  899. XX                {
  900. XX                if ( typchr == 's' )    /* string? */
  901. XX                    continue;    /* don't want numbers, then */
  902. XX                }
  903. XX                else if ( *ncp == '-' )
  904. XX                if ( optarg > 0 ) /* end optional args? */
  905. XX                {
  906. XX                    /* Eat the arg, too, if necessary */
  907. XX                    if ( list_cnt == 0 )
  908. XX                    if ( typchr == 's' )
  909. XX                        va_arg( argl, char * );
  910. XX                    else
  911. XX                        va_arg( argl, ptr );
  912. XX                    break;
  913. XX                }
  914. XX                else
  915. XX                    continue;
  916. XX                else if ( typchr != 's' )
  917. XX                continue;    /* not number, keep looking */
  918. XX                
  919. XX                /* 
  920. XX                 * Otherwise usable argument may already
  921. XX                 * be used.  (Must check this after
  922. XX                 * checking for flag, though.)
  923. XX                 */
  924. XX                if (arg_used[cnt]) continue;
  925.  
  926. XX                /* 
  927. XX                 * If it's a comma-and-or-space-separated
  928. XX                 * list then count how many, and separate
  929. XX                 * the list into an array of strings.
  930. XX                 */
  931. XX                if ( comma_list )
  932. XX                {
  933. XX                register char * s;
  934. XX                int pass;
  935.  
  936. XX                /* 
  937. XX                 * On pass 0, just count them.  On
  938. XX                 * pass 1, null terminate each string 
  939. XX                 */
  940. XX                for ( pass = 0; pass <= 1; pass++ )
  941. XX                {
  942. XX                    for ( s = ncp; *s != '\0'; )
  943. XX                    {
  944. XX                    if ( pass )
  945. XX                        strlist[list_cnt] = s;
  946. XX                    while ( (c = *s) != '\0' && c != ' ' &&
  947. XX                        c != '\t' && c != ',' )
  948. XX                        s++;
  949. XX                    if ( pass )
  950. XX                        *s = '\0';
  951.  
  952. XX                    list_cnt++;    /* count separators */
  953. XX                    /* 
  954. XX                     * Two commas in a row give a null
  955. XX                     * string, but two spaces
  956. XX                     * don't.  Also skip spaces
  957. XX                     * after a comma.
  958. XX                     */
  959. XX                    if ( c != '\0' )
  960. XX                        while ( *++s == ' ' || *s == '\t' )
  961. XX                        ;
  962. XX                    }
  963. XX                    if ( pass == 0 )
  964. XX                    {
  965. XX                    strlist = NEW( char *, list_cnt );
  966. XX                    list_cnt = 0;
  967. XX                    }
  968. XX                }
  969. XX                }
  970. XX                else if ( list_of )
  971. XX                list_cnt++;   /* getting them one at a time */
  972. XX                /* 
  973. XX                 * If it's either type of list, then alloc
  974. XX                 * storage space for the returned values
  975. XX                 * (except that comma-separated string
  976. XX                 * lists already are done).
  977. XX                 */
  978. XX                if ( list_of )
  979. XX                {
  980. XX                if ( list_cnt == 1 || comma_list )
  981. XX                    switch( typchr )
  982. XX                    {
  983. XX                    case 's':
  984. XX                        if ( !comma_list )
  985. XX                        strlist = NEW( char *, 1 );
  986. XX                        aptr = (ptr) &strlist[0];
  987. XX                        break;
  988. XX                    case 'n':
  989. XX                    case 'd':
  990. XX                    case 'o':
  991. XX                    case 'x':
  992. XX                        intlist = NEW( int, list_cnt );
  993. XX                        aptr = (ptr) &intlist[0];
  994. XX                        break;
  995. XX                    case 'N':
  996. XX                    case 'D':
  997. XX                    case 'O':
  998. XX                    case 'X':
  999. XX                        longlist = NEW( long, list_cnt );
  1000. XX                        aptr = (ptr) &longlist[0];
  1001. XX                        break;
  1002. XX                    case 'f':
  1003. XX                        fltlist = NEW( float, list_cnt );
  1004. XX                        aptr = (ptr) &fltlist[0];
  1005. XX                        break;
  1006. XX                    case 'F':
  1007. XX                        dbllist = NEW( double, list_cnt );
  1008. XX                        aptr = (ptr) &dbllist[0];
  1009. XX                        break;
  1010. XX                    }
  1011. XX                else
  1012. XX                    switch( typchr )
  1013. XX                    {
  1014. XX                    case 's':
  1015. XX                        strlist = RENEW( char *, strlist,
  1016. XX                                 list_cnt );
  1017. XX                        aptr = (ptr) &strlist[list_cnt-1];
  1018. XX                        break;
  1019. XX                    case 'n':
  1020. XX                    case 'd':
  1021. XX                    case 'o':
  1022. XX                    case 'x':
  1023. XX                        intlist = RENEW( int, intlist,
  1024. XX                                 list_cnt );
  1025. XX                        aptr = (ptr) &intlist[list_cnt-1];
  1026. XX                        break;
  1027. XX                    case 'N':
  1028. XX                    case 'D':
  1029. XX                    case 'O':
  1030. XX                    case 'X':
  1031. XX                        longlist = RENEW( long, longlist,
  1032. XX                                  list_cnt );
  1033. XX                        aptr = (ptr) &longlist[list_cnt-1];
  1034. XX                        break;
  1035. XX                    case 'f':
  1036. XX                        fltlist = RENEW( float, fltlist,
  1037. XX                                 list_cnt );
  1038. XX                        aptr = (ptr) &fltlist[list_cnt-1];
  1039. XX                        break;
  1040. XX                    case 'F':
  1041. XX                        dbllist = RENEW( double, dbllist,
  1042. XX                                 list_cnt );
  1043. XX                        aptr = (ptr) &dbllist[list_cnt-1];
  1044. XX                        break;
  1045. XX                    }
  1046. XX                }
  1047. XX                else
  1048. XX                aptr = va_arg( argl, ptr );
  1049.  
  1050. XX                if ( typchr == 's' )
  1051. XX                {
  1052. XX                if ( ! comma_list )
  1053. XX                    *(char **)aptr = ncp;
  1054. XX                }
  1055. XX                else
  1056. XX                {
  1057. XX                nopt = 0;
  1058. XX                do {
  1059. XX                    /* 
  1060. XX                     * Need to update aptr if parsing
  1061. XX                     * a comma list
  1062. XX                     */
  1063. XX                    if ( comma_list && nopt > 0 )
  1064. XX                    {
  1065. XX                    ncp = strlist[nopt];
  1066. XX                    switch( typchr )
  1067. XX                    {
  1068. XX                        case 'n':
  1069. XX                        case 'd':
  1070. XX                        case 'o':
  1071. XX                        case 'x':
  1072. XX                        aptr = (ptr) &intlist[nopt];
  1073. XX                        break;
  1074. XX                        case 'N':
  1075. XX                        case 'D':
  1076. XX                        case 'O':
  1077. XX                        case 'X':
  1078. XX                        aptr = (ptr) &longlist[nopt];
  1079. XX                        break;
  1080. XX                        case 'f':
  1081. XX                        aptr = (ptr) &fltlist[nopt];
  1082. XX                        break;
  1083. XX                        case 'F':
  1084. XX                        aptr = (ptr) &dbllist[nopt];
  1085. XX                        break;
  1086. XX                    }
  1087. XX                    }
  1088. XX                    /* 
  1089. XX                     * Do conversion for n and N types
  1090. XX                     */
  1091. XX                    tmpflg = typchr;
  1092. XX                    if (typchr == 'n' || typchr == 'N' )
  1093. XX                    if (*ncp != '0')
  1094. XX                        tmpflg = 'd';
  1095. XX                    else if (*(ncp+1) == 'x' ||
  1096. XX                         *(ncp+1) == 'X')
  1097. XX                    {
  1098. XX                        tmpflg = 'x';
  1099. XX                        ncp += 2;
  1100. XX                    }
  1101. XX                    else
  1102. XX                        tmpflg = 'o';
  1103. XX                    if (typchr == 'N')
  1104. XX                    toupper( tmpflg );
  1105.  
  1106.  
  1107. XX#ifndef    QUICK
  1108. XX                    cntrl[1] = tmpflg;/* put in conversion */
  1109. XX                    if (sscanf (ncp, cntrl, aptr, junk) != 1)
  1110. XX                    ERROR (Bad numeric argument);
  1111. XX#else
  1112. XX                    if (numcvt(ncp, tmpflg, aptr) != 1)
  1113. XX                    ERROR (Bad numeric argument);
  1114. XX#endif
  1115. XX                } while ( comma_list && ++nopt < list_cnt );
  1116. XX                }
  1117. XX                check += cnt;
  1118. XX                arg_used[cnt] = 1;
  1119. XX                required = NO;
  1120. XX                /*
  1121. XX                 * If not looking for multiple args,
  1122. XX                 * then done, otherwise, keep looking.
  1123. XX                 */
  1124. XX                if ( !( list_of && !comma_list ) )
  1125. XX                break;
  1126. XX                else
  1127. XX                continue;
  1128. XX            }
  1129. XX            if (required)
  1130. XX                switch (typchr)
  1131. XX                {
  1132. XX                case 'x': 
  1133. XX                case 'X': 
  1134. XX                    ERROR (missing hexadecimal argument);
  1135. XX                case 's': 
  1136. XX                    ERROR (missing string argument);
  1137. XX                case 'o': 
  1138. XX                case 'O': 
  1139. XX                    ERROR (missing octal argument);
  1140. XX                case 'd': 
  1141. XX                case 'D': 
  1142. XX                    ERROR (missing decimal argument);
  1143. XX                case 'f': 
  1144. XX                case 'F': 
  1145. XX                    ERROR (missing floating argument);
  1146. XX                case 'n':
  1147. XX                case 'N':
  1148. XX                    ERROR (missing numeric argument);
  1149. XX                }
  1150. XX            if ( list_cnt > 0 )
  1151. XX            {
  1152. XX                *cnt_arg = list_cnt;
  1153. XX                switch ( typchr )
  1154. XX                {
  1155. XX                case 's':
  1156. XX                    *va_arg( argl, char *** ) = strlist;
  1157. XX                    break;
  1158. XX                case 'n':
  1159. XX                case 'd':
  1160. XX                case 'o':
  1161. XX                case 'x':
  1162. XX                    *va_arg( argl, int ** ) = intlist;
  1163. XX                    break;
  1164. XX                case 'N':
  1165. XX                case 'D':
  1166. XX                case 'O':
  1167. XX                case 'X':
  1168. XX                    *va_arg( argl, long ** ) = longlist;
  1169. XX                    break;
  1170. XX                case 'f':
  1171. XX                    *va_arg( argl, float ** ) = fltlist;
  1172. XX                    break;
  1173. XX                case 'F':
  1174. XX                    *va_arg( argl, double **) = dbllist;
  1175. XX                    break;
  1176. XX                }
  1177. XX                if ( typchr != 's' )
  1178. XX                free( (char *) strlist );
  1179. XX            }
  1180. XX            else if ( cnt >= argc )
  1181. XX            {
  1182. XX                /* Fell off end looking, so must eat the arg */
  1183. XX                if ( typchr == 's' )
  1184. XX                va_arg( argl, char * );
  1185. XX                else
  1186. XX                va_arg( argl, ptr );
  1187. XX            }
  1188. XX            break;
  1189. XX            default:         /* error */
  1190. XX            fprintf (stderr, "error in call to scanargs\n");
  1191. XX            return (0);
  1192. XX        }
  1193. XX    }
  1194. XX    }
  1195.  
  1196. XX    /*  Count up empty flags */
  1197. XX    for (cnt=1; cnt<argc; cnt++)
  1198. XX    if (argv[cnt][0] == '-' && argv[cnt][1] == '-' && argv[cnt][2] == 0
  1199. XX        && !arg_used[cnt] )
  1200. XX        check += cnt;
  1201.  
  1202. XX    /* sum from 1 to N = n*(n+1)/2 used to count up checks */
  1203. XX    if (check != (((argc - 1) * argc) / 2))
  1204. XX    ERROR (extra arguments not processed);
  1205.  
  1206. XX    free(arg_used);
  1207. XX    return (1);
  1208.  
  1209. XXerror: 
  1210. XX    fprintf (stderr, "usage : ");
  1211. XX    if (*(cp = format) != ' ')
  1212. XX    {
  1213. XX    if ( *cp == '%' )
  1214. XX    {
  1215. XX        /* 
  1216. XX         * This is bogus, but until everyone can agree on a name
  1217. XX         * for (rindex/strrchr) ....
  1218. XX         */
  1219. XX        for ( cp = argv[0]; *cp != '\0'; cp++ )
  1220. XX        ;            /* find the end of the string */
  1221. XX        for ( ; cp > argv[0] && *cp != '/'; cp-- )
  1222. XX        ;            /* find the last / */
  1223. XX        if ( *cp == '/' )
  1224. XX        cp++;
  1225. XX        fprintf( stderr, "%s", cp );
  1226.  
  1227. XX        cp = format + 1;        /* reset to where it should be */
  1228. XX    }
  1229. XX    while (putc (*cp++, stderr) != ' ');
  1230. XX    }
  1231. XX    else
  1232. XX    fprintf (stderr, "?? ");
  1233. XX    while (*cp == ' ')
  1234. XX    cp++;
  1235. XX    prformat (cp, NO);
  1236. XX    free(arg_used);
  1237. XX    return 0;
  1238. XX}
  1239.  
  1240. XXstatic char *
  1241. XXprformat (format, recurse)
  1242. XXchar   *format;
  1243. XX{
  1244. XX    register char  *cp;
  1245. XX    bool    required, comma_list;
  1246. XX    int    list_of;
  1247.  
  1248. XX    cp = format;
  1249. XX    if (recurse)
  1250. XX    putc (' ', stderr);
  1251.  
  1252. XX    required = NO;
  1253. XX    list_of = 0;
  1254. XX    comma_list = NO;
  1255. XX    while (*cp)
  1256. XX    {
  1257. XX    switch (*cp)
  1258. XX    {
  1259. XX        default: 
  1260. XX        cp++;
  1261. XX        break;
  1262. XX        case ' ':
  1263. XX        case '\t':
  1264. XX        case '\n':
  1265. XX        putc(*cp, stderr);
  1266. XX        format = ++cp;
  1267. XX        break;
  1268. XX        case '!': 
  1269. XX        required = YES;
  1270. XX        case '%': 
  1271. XXreswitch:
  1272. XX        switch (*++cp)
  1273. XX        {
  1274. XX            case ',':
  1275. XX            comma_list++;
  1276. XX            case '*':
  1277. XX            list_of++;
  1278. XX            goto reswitch;
  1279.  
  1280. XX            case '$':        /* "rest" of argument list */
  1281. XX            if (!required)
  1282. XX                putc ('[', stderr);
  1283. XX            for (; format < cp - 1 - list_of; format++)
  1284. XX                putc (*format, stderr);
  1285. XX            fputs( " ...", stderr );
  1286. XX            if ( !required )
  1287. XX                putc( ']', stderr );
  1288. XX            break;
  1289.  
  1290. XX            case '-':         /* flags */
  1291. XX            if (!required)
  1292. XX                putc ('[', stderr);
  1293. XX            putc ('-', stderr);
  1294.  
  1295. XX            if (cp - format > 2 + list_of)
  1296. XX                putc ('{', stderr);
  1297. XX            cp = format;
  1298. XX            while (*cp != '%' && *cp != '!')
  1299. XX                putc (*cp++, stderr);
  1300. XX            if (cp - format > 1 + list_of)
  1301. XX                putc ('}', stderr);
  1302. XX            cp += 2;    /* skip !- or %- */
  1303. XX            if (*cp && !isspace(*cp))
  1304. XX                cp = prformat (cp, YES);
  1305. XX                    /* this is a recursive call */
  1306. XX            if (!required)
  1307. XX                putc (']', stderr);
  1308. XX            break;
  1309. XX            case 's':         /* char string */
  1310. XX            case 'd':         /* decimal # */
  1311. XX            case 'o':         /* octal # */
  1312. XX            case 'x':         /* hexadecimal # */
  1313. XX            case 'f':         /* floating # */
  1314. XX            case 'D':         /* long decimal # */
  1315. XX            case 'O':         /* long octal # */
  1316. XX            case 'X':         /* long hexadecimal # */
  1317. XX            case 'F':         /* double precision floating # */
  1318. XX            case 'n':        /* numeric arg (C format) */
  1319. XX            case 'N':        /* long numeric arg */
  1320. XX            if (!required)
  1321. XX                putc ('[', stderr);
  1322. XX            for (; format < cp - 1 - list_of; format++)
  1323. XX                putc (*format, stderr);
  1324. XX            if ( list_of != 0 )
  1325. XX            {
  1326. XX                if ( comma_list )
  1327. XX                putc( ',', stderr );
  1328. XX                else
  1329. XX                putc( ' ', stderr );
  1330. XX                fputs( "...", stderr );
  1331. XX            }
  1332. XX            if (!required)
  1333. XX                putc (']', stderr);
  1334. XX            break;
  1335. XX            default: 
  1336. XX            break;
  1337. XX        }
  1338. XX        required = NO;
  1339. XX        list_of = NO;
  1340. XX        comma_list = NO;
  1341. XX        if (*cp)        /* check for end of string */
  1342. XX            format = ++cp;
  1343. XX        if (*cp && !isspace(*cp))
  1344. XX            putc (' ', stderr);
  1345. XX    }
  1346. XX    if (recurse && isspace(*cp))
  1347. XX        break;
  1348. XX    }
  1349. XX    if (!recurse)
  1350. XX    putc ('\n', stderr);
  1351. XX    return (cp);
  1352. XX}
  1353.  
  1354. XX/* 
  1355. XX * isnum - determine whether a string MIGHT represent a number.
  1356. XX * typchr indicates the type of argument we are looking for, and
  1357. XX * determines the legal character set.  If comma_list is YES, then
  1358. XX * space and comma are also legal characters.
  1359. XX */
  1360. XXstatic bool
  1361. XXisnum( str, typchr, comma_list )
  1362. XXregister char * str;
  1363. XXchar typchr;
  1364. XXbool comma_list;
  1365. XX{
  1366. XX    register char * allowed, * digits, * cp;
  1367. XX    bool hasdigit = NO;
  1368.  
  1369. XX    switch( typchr )
  1370. XX    {
  1371. XX    case 'n':
  1372. XX    case 'N':
  1373. XX        allowed = " \t,+-x0123456789abcdefABCDEF";
  1374. XX        break;
  1375. XX    case 'd':
  1376. XX    case 'D':
  1377. XX        allowed = " \t,+-0123456789";
  1378. XX        break;
  1379. XX    case 'o':
  1380. XX    case 'O':
  1381. XX        allowed = " \t,01234567";
  1382. XX        break;
  1383. XX    case 'x':
  1384. XX    case 'X':
  1385. XX        allowed = " \t,0123456789abcdefABCDEF";
  1386. XX        break;
  1387. XX    case 'f':
  1388. XX    case 'F':
  1389. XX        allowed = " \t,+-eE.0123456789";
  1390. XX        break;
  1391. XX    case 's':            /* only throw out decimal numbers */
  1392. XX    default:
  1393. XX        allowed = " \t,+-.0123456789";
  1394. XX        break;
  1395. XX    }
  1396. XX    digits = allowed;
  1397. XX    while ( *digits != '0' )
  1398. XX    digits++;
  1399. XX    if ( ! comma_list )
  1400. XX    allowed += 3;              /* then don't allow space, tab, comma */
  1401.  
  1402. XX    while ( *str != '\0' )
  1403. XX    {
  1404. XX        for ( cp = allowed; *cp != '\0' && *cp != *str; cp++ )
  1405. XX            ;
  1406. XX        if ( *cp == '\0' )
  1407. XX        return NO;             /* if not in allowed chars, not number */
  1408. XX    if ( cp - digits >= 0 )
  1409. XX        hasdigit = YES;
  1410. XX    str++;
  1411. XX    }
  1412. XX    return hasdigit;
  1413. XX}
  1414.  
  1415. XX#ifdef    QUICK
  1416. XXnumcvt(str, conv, val)
  1417. XXregister char *str;
  1418. XXchar conv;
  1419. XXint *val;
  1420. XX{
  1421. XX    int base, neg = 0;
  1422. XX    register unsigned int d;
  1423. XX    long retval = 0;
  1424. XX    register char *digits;
  1425. XX    extern char *index();
  1426. XX    if (conv == 'o' || conv == 'O')
  1427. XX    base = 8;
  1428. XX    else if (conv == 'd' || conv == 'D')
  1429. XX    base = 10;
  1430. XX    else if (conv == 'x' || conv == 'X')
  1431. XX    base = 16;
  1432. XX    else
  1433. XX    return 0;
  1434.  
  1435. XX    if (*str == '-')
  1436. XX    {
  1437. XX    neg = 1;
  1438. XX    str++;
  1439. XX    }
  1440. XX    while (*str)
  1441. XX    {
  1442. XX    if (*str >= '0' && *str < '0'+base)
  1443. XX        d = *str - '0';
  1444. XX    else if (base == 16 && *str >= 'a' && *str <= 'f')
  1445. XX        d = 10 + *str - 'a';
  1446. XX    else if (base == 16 && *str >= 'A' && *str <= 'F')
  1447. XX        d = 10 + *str - 'A';
  1448. XX    else
  1449. XX        return 0;
  1450. XX    retval = retval*base + d;
  1451. XX    str++;
  1452. XX    }
  1453. XX    if (neg)
  1454. XX    retval = -retval;
  1455. XX    if (conv == 'D' || conv == 'O' || conv == 'X')
  1456. XX    *(long *) val = retval;
  1457. XX    else
  1458. XX    *val = (int) retval;
  1459. XX    return 1;
  1460. XX}
  1461. XX#endif    QUICK
  1462. @//E*O*F scanargs.c//
  1463. chmod u=rw,g=rw,o=rw scanargs.c
  1464.  
  1465. echo Inspecting for damage in transit...
  1466. temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
  1467. trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
  1468. cat > $temp <<\!!!
  1469.       29     244    1409 README
  1470.       15      73     436 Makefile
  1471.       96     593    3593 newscnt.1
  1472.      437    1727   11200 newscnt.c
  1473.      833    3103   19707 scanargs.c
  1474.     1410    5740   36345 total
  1475. !!!
  1476. wc  README Makefile newscnt.1 newscnt.c scanargs.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
  1477. if test -s $dtemp
  1478. then echo "Ouch [diff of wc output]:" ; cat $dtemp
  1479. else echo "No problems found."
  1480. fi
  1481. exit 0
  1482.